package com.yugao.fintech.libra.common.util.uuid;

import com.github.pagehelper.util.StringUtil;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;

/**
 * 雪花算法ID生成器
 */
public class Snowflake {
    //机器id
    private final long workerId;
    // 机房id
    private final long datacenterId;
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    // 0，并发控制 毫秒内序列(0~4095)
    private volatile long sequence = 0L;
    private long lastTimestamp = -1L;

    private Snowflake() {
        //支持的最大数据标识id，结果是31
        this.datacenterId = getDatacenterId();
        //支持的最大机器id，结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
        this.workerId = getMaxWorkerId(this.datacenterId);
    }

    public static Snowflake getInstance() {
        return MyInstance.SINGLETON;
    }

    /**
     * 获取 maxWorkerId
     */
    private long getMaxWorkerId(long datacenterId) {
        long maxWorkerId = ~(-1L << workerIdBits);
        StringBuilder worker = new StringBuilder();
        worker.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (StringUtil.isNotEmpty(name)) {
            worker.append(name.split("@")[0]);
        }
        //MAC + PID 的 hashcode 获取16个低位
        return (worker.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * 获取机房id
     */
    private long getDatacenterId() {
        long maxDatacenterId = ~(-1L << datacenterIdBits);
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                if (null != mac) {
                    id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                    id = id % (maxDatacenterId + 1);
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return id;
    }

    // 生成一个全局唯一的id
    public synchronized long nextId() {
        // 获取当前时间戳，单位是毫秒
        long timestamp = timeMillis();

        if (timestamp < lastTimestamp) {
            return nextId();
        }

        // 假设在同一个毫秒内，又发送了一个请求生成一个id
        // 这个时候就得把 sequence 序号给递增1，最多就是4096
        long sequenceBits = 12L;
        if (lastTimestamp == timestamp) {
            //避免传递的sequence超过了4096这个范围
            long sequenceMask = ~(-1L << sequenceBits);
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = nextTimeMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }

        //上次生成ID的时间截,单位:毫秒
        lastTimestamp = timestamp;

        // 生成一个64bit的id
        // 先将当前时间戳左移，放到41 bit；将机房id左移放到5 bit,将机器id左移放到5 bit；将序号放最后12 bit
        // 最后拼接起来成一个64 bit的二进制数字，转换成10进制就是个long型
        //数据标识id向左移17位(12+5)
        long datacenterIdShift = sequenceBits + workerIdBits;
        long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        long twePouch = 1288834974657L;
        return ((timestamp - twePouch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << sequenceBits) | sequence;
    }

    private long nextTimeMillis(long lastTimestamp) {
        long timestamp = timeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = timeMillis();
        }
        return timestamp;
    }

    /**
     * 时间戳
     */
    private long timeMillis() {
        return System.currentTimeMillis();
    }

    private static class MyInstance {
        private final static Snowflake SINGLETON = new Snowflake();
    }
}
